home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/signal.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- /*
- * 680x0 support by Hamish Macdonald
- */
-
- #include <asm/system.h>
- #include <asm/segment.h>
-
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/traps.h>
- #include <linux/signal.h>
- #include <linux/errno.h>
- #include <linux/wait.h>
- #include <linux/ptrace.h>
- #include <linux/unistd.h>
-
- #define offsetof(type, member) ((size_t)(&((type *)0)->member))
-
- #define _S(nr) (1<<((nr)-1))
- #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
- extern int core_dump(long signr,struct pt_regs * regs);
- asmlinkage int sys_wait4(pid_t pid,unsigned long * stat_addr, int options,
- struct rusage *ru);
-
- struct sigcontext {
- unsigned long sc_mask; /* old sigmask */
- unsigned long sc_usp; /* old user stack pointer */
- unsigned long sc_d0;
- unsigned long sc_d1;
- unsigned long sc_a0;
- unsigned long sc_a1;
- unsigned short sc_sr;
- unsigned long sc_pc;
- unsigned short sc_formatvec;
- };
-
- asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
-
- /*
- * This sets regs->usp even though we don't actually use sigstacks yet..
- */
- asmlinkage int sys_sigreturn(unsigned long __unused)
- {
- struct sigcontext context;
- struct frame * regs;
- int fsize = 0;
- int formatvec = 0;
- unsigned long fp;
-
- /* get stack frame pointer */
- regs = (struct frame *) &__unused;
-
- /* get previous context (including pointer to possible extra junk) */
- memcpy_fromfs(&context,(void *) regs->usp, sizeof(context));
- fp = regs->usp + sizeof (context);
-
- /* restore signal mask */
- current->blocked = context.sc_mask & _BLOCKABLE;
-
- /* restore passed registers */
- regs->d0 = context.sc_d0;
- regs->regs[0] = context.sc_d1;
- regs->regs[7] = context.sc_a0;
- regs->regs[8] = context.sc_a1;
- regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
- regs->pc = context.sc_pc;
- regs->usp = context.sc_usp;
- formatvec = context.sc_formatvec;
- regs->format = formatvec >> 12;
- regs->vector = formatvec & 0xfff;
-
- switch (regs->format) {
- case 0x0:
- fsize = 0;
- break;
- case 0x2:
- fsize = sizeof(regs->un.fmt2);
- break;
- case 0x9:
- fsize = sizeof (regs->un.fmt9);
- break;
- case 0xA:
- fsize = sizeof (regs->un.fmta);
- break;
- case 0xB:
- fsize = sizeof (regs->un.fmtb);
- break;
- default:
- /*
- * user process trying to return with wierd frame format
- * SIGILL the sucker.
- */
- send_sig (SIGILL, current, 1);
- break;
- }
-
- /* OK. Make room on the supervisor stack for the extra junk,
- * if necessary.
- */
-
- if (fsize) {
- __asm__ __volatile__
- ("movel %0,a0\n\t"
- "subl %2,a0\n\t" /* make room on stack */
- "movel a0,sp\n\t" /* set stack pointer */
- "1: movew %0@+,a0@+\n\t"
- " dbra %3,1b\n\t"
- "movel sp,a0\n\t"
- "addl %4,a0\n\t" /* add offset of fmt stuff */
- "lsrl #1,%2\n\t"
- "subql #1,%2\n\t"
- "2: movesw %5@+,d0\n\t"
- " movew d0,a0@+\n\t"
- " dbra %2,2b\n\t"
- "bra _ret_from_exception"
- : "=a" (regs)
- : "0" (regs), "d" (fsize),
- "d" (offsetof(struct frame,un)/2-1),
- "g" (offsetof(struct frame,un)), "a" (fp)
- : "a0", "a1", "d0");
- for (;;)
- ;
- /* NOTREACHED */
- }
-
- return regs->d0;
- }
-
- /*
- * Set up a signal frame...
- *
- * This routine is somewhat complicated by the fact that if the
- * kernel may be entered by an exception other than a system call;
- * e.g. a bus error or other "bad" exception. If this is the case,
- * then *all* the context on the kernel stack frame must be saved.
- *
- * For a large number of exceptions, the stack frame format is the same
- * as that which will be created when the process traps back to the kernel
- * when finished executing the signal handler. In this case, nothing
- * must be done. This is exception frame format "0". For exception frame
- * formats "2", "9", "A" and "B", the extra information on the frame must
- * be saved. This information is saved on the user stack and restored
- * when the signal handler is returned.
- *
- * Note that this code must be extended to handle frame formats "3", "4"
- * and "7" to support the 68040.
- *
- * The format of the user stack when executing the signal handler is:
- *
- * usp -> RETADDR (points to code below)
- * signum (parm #1)
- * sigcode (parm #2 ; unused)
- * scp (parm #3 ; sigcontext pointer, pointer to #1 below)
- * code1 (addaw #20,sp) ; pop parms and code off stack
- * code2 (moveq #119,d0; trap #0) ; sigreturn syscall
- * #1| oldmask
- * | old usp
- * | d0 (first saved reg)
- * | d1
- * | a0
- * | a1
- * | sr (saved status register)
- * | pc (old pc; one to return to)
- * | forvec (format and vector word of old supervisor stack frame)
- *
- * These are optionally followed by some extra stuff, depending on the
- * stack frame interrupted. This is 1 longword for format "2", 3
- * longwords for format "9", 6 longwords for format "A", and 21
- * longwords for format "B".
- *
- * XXX This format must be changed to save floating point context also.
- */
-
- #define UFRAME_SIZE(fs) (sizeof(struct sigcontext) + 8 + fs/4)
-
- static void setup_frame (struct sigaction * sa, unsigned long **fp, unsigned long pc,
- struct frame *regs, int signr, unsigned long oldmask)
- {
- struct sigcontext context;
- unsigned long *frame, *tframe;
-
- switch (regs->format) {
- case 0x0:
- frame = *fp - UFRAME_SIZE(0);
- verify_area(VERIFY_WRITE,frame,UFRAME_SIZE(0)*4);
- break;
- case 0x2:
- frame = *fp - UFRAME_SIZE(sizeof(regs->un.fmt2));
- verify_area(VERIFY_WRITE,frame,
- UFRAME_SIZE(sizeof(regs->un.fmt2))*4);
- put_fs_long(regs->un.fmt2.iaddr, frame+UFRAME_SIZE(0));
- regs->stkadj = sizeof(regs->un.fmt2);
- break;
- case 0x9:
- frame = *fp - UFRAME_SIZE(sizeof(regs->un.fmt9));
- verify_area(VERIFY_WRITE,frame,
- UFRAME_SIZE(sizeof(regs->un.fmt9))*4);
- memcpy_tofs (frame+UFRAME_SIZE(0), ®s->un.fmt9,
- sizeof(regs->un.fmt9));
- regs->stkadj = sizeof(regs->un.fmt9);
- break;
- case 0xA:
- frame = *fp - UFRAME_SIZE(sizeof(regs->un.fmta));
- verify_area(VERIFY_WRITE,frame,
- UFRAME_SIZE(sizeof(regs->un.fmta))*4);
- memcpy_tofs (frame+UFRAME_SIZE(0), ®s->un.fmta,
- sizeof(regs->un.fmta));
- regs->stkadj = sizeof(regs->un.fmta);
- break;
- case 0xB:
- frame = *fp - UFRAME_SIZE(sizeof(regs->un.fmtb));
- verify_area(VERIFY_WRITE,frame,
- UFRAME_SIZE(sizeof(regs->un.fmtb))*4);
- memcpy_tofs (frame+UFRAME_SIZE(0), ®s->un.fmtb,
- sizeof(regs->un.fmtb));
- regs->stkadj = sizeof(regs->un.fmtb);
- break;
- default:
- printk ("setup_frame: Unknown frame format %#x\n",
- regs->format);
- panic ("setup_frame: Unknown frame format");
- }
-
- /* set up the "normal" stack seen by the signal handler */
- tframe = frame;
-
- /* return address points to code on stack */
- put_fs_long((ulong)(frame+4), tframe); tframe++;
- put_fs_long(signr, tframe); tframe++;
- put_fs_long(0, tframe); tframe++; /* "code" parameter. Unused */
- /* "scp" parameter. points to sigcontext */
- put_fs_long((ulong)(frame+6), tframe); tframe++;
-
- /* set up the return code... */
- put_fs_long(0xdefc0014,tframe); tframe++; /* addaw #20,sp */
- put_fs_long(0x70774e40,tframe); tframe++; /* moveq #119,d0; trap #0 */
-
- /* Flush caches so the instructions will be correctly executed. (MA) */
- cache_push_v ((unsigned long)frame, (int)tframe - (int)frame);
-
- /* setup and copy the sigcontext structure */
- context.sc_mask = oldmask;
- context.sc_usp = (unsigned long)*fp;
- context.sc_d0 = regs->d0;
- context.sc_d1 = regs->regs[0];
- context.sc_a0 = regs->regs[7];
- context.sc_a1 = regs->regs[8];
- context.sc_sr = regs->sr;
- context.sc_pc = pc;
- context.sc_formatvec = regs->format << 12 | regs->vector;
- memcpy_tofs (tframe, &context, sizeof(context));
-
- /*
- * no matter what frame format we were using before, we
- * will do the "RTE" using a normal 4 word frame.
- */
- regs->format = 0;
-
- /* "return" new usp to caller */
- *fp = frame;
- }
-
- /*
- * Note that 'init' is a special process: it doesn't get signals it doesn't
- * want to handle. Thus you cannot kill init even with a SIGKILL even by
- * mistake.
- *
- * Note that we go through the signals twice: once to check the signals
- * that the kernel can handle, and then we build all the user-level signal
- * handling stack-frames in one go after that.
- */
- asmlinkage int do_signal(unsigned long oldmask, struct frame * regs)
- {
- unsigned long mask = ~current->blocked;
- unsigned long handler_signal = 0;
- unsigned long *frame = NULL;
- unsigned long pc = 0;
- unsigned long signr;
- struct sigaction * sa;
-
- while ((signr = current->signal & mask)) {
- __asm__("bfffo %2,#0,#0,%1\n\t"
- "bfclr %0,%1,#1\n\t"
- "negl %1\n\t"
- "addl #31,%1"
- :"=m" (current->signal),"=r" (signr)
- :"1" (signr));
- sa = current->sigaction + signr;
- signr++;
-
- if (signr == SIGSEGV)
- printk ("%s: SIGSEGV from pc %#lx, regs=%p\n",
- current->comm, regs->pc, regs);
-
- if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
- current->exit_code = signr;
- current->state = TASK_STOPPED;
- notify_parent(current);
- schedule();
- if (!(signr = current->exit_code))
- continue;
- current->exit_code = 0;
- if (signr == SIGSTOP)
- continue;
- if (_S(signr) & current->blocked) {
- current->signal |= _S(signr);
- continue;
- }
- sa = current->sigaction + signr - 1;
- }
- if (sa->sa_handler == SIG_IGN) {
- if (signr != SIGCHLD)
- continue;
- /* check for SIGCHLD: it's special */
- while (sys_waitpid(-1,NULL,WNOHANG) > 0)
- /* nothing */;
- continue;
- }
- if (sa->sa_handler == SIG_DFL) {
- if (current->pid == 1)
- continue;
- switch (signr) {
- case SIGCONT: case SIGCHLD: case SIGWINCH:
- continue;
-
- case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
- if (current->flags & PF_PTRACED)
- continue;
- current->state = TASK_STOPPED;
- current->exit_code = signr;
- if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
- SA_NOCLDSTOP))
- notify_parent(current);
- schedule();
- continue;
-
- case SIGQUIT: case SIGILL: case SIGTRAP:
- case SIGIOT: case SIGFPE: case SIGSEGV:
- if (core_dump(signr,(struct pt_regs *)regs))
- signr |= 0x80;
- /* fall through */
- default:
- current->signal |= _S(signr & 0x7f);
- do_exit(signr);
- }
- }
- /*
- * OK, we're invoking a handler
- */
- if (regs->orig_d0 >= 0) {
- if (regs->d0 == -ERESTARTNOHAND ||
- (regs->d0 == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
- regs->d0 = -EINTR;
- }
- handler_signal |= 1 << (signr-1);
- mask &= ~sa->sa_mask;
- }
- if (regs->orig_d0 >= 0 &&
- (regs->d0 == -ERESTARTNOHAND ||
- regs->d0 == -ERESTARTSYS ||
- regs->d0 == -ERESTARTNOINTR)) {
- regs->d0 = regs->orig_d0;
- regs->pc -= 2;
- }
- if (!handler_signal) /* no handler will be called - return 0 */
- return 0;
- pc = regs->pc;
- frame = (unsigned long *)regs->usp;
- signr = 1;
- sa = current->sigaction;
- for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
- if (mask > handler_signal)
- break;
- if (!(mask & handler_signal))
- continue;
- setup_frame(sa,&frame,pc,regs,signr,oldmask);
- pc = (unsigned long) sa->sa_handler;
- if (sa->sa_flags & SA_ONESHOT)
- sa->sa_handler = NULL;
- /* force a supervisor-mode page-in of the signal handler to reduce races */
- __asm__ __volatile__("movesb %0,d0": :"m" (*(char *)pc):"d0");
- current->blocked |= sa->sa_mask;
- oldmask |= sa->sa_mask;
- }
- regs->usp = (unsigned long) frame;
- regs->pc = pc;
-
- /*
- * if setup_frame saved some extra frame junk, we need to
- * skip over that stuff when doing the RTE. This means we have
- * to move the machine portion of the stack frame to where the
- * "RTE" instruction expects it. The signal that we need to
- * do this is that regs->stkadj is nonzero.
- */
- if (regs->stkadj) {
- struct frame *tregs =
- (struct frame *)((ulong)regs + regs->stkadj);
-
- tregs->sr = regs->sr;
- tregs->pc = regs->pc;
- tregs->format = regs->format;
- tregs->vector = regs->vector;
- }
-
- return 1;
- }
-